/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.classdiagram;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.gef.palette.MarqueeToolEntry;
import org.eclipse.gef.palette.PaletteContainer;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.palette.PaletteEntry;
import org.eclipse.gef.palette.PaletteGroup;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.palette.PaletteSeparator;
import org.eclipse.gef.palette.PaletteStack;
import org.eclipse.gef.palette.PanningSelectionToolEntry;
import org.eclipse.gef.palette.ToolEntry;
import org.eclipse.gef.ui.palette.FlyoutPaletteComposite.FlyoutPreferences;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.uml2.uml.Abstraction;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.ClassifierTemplateParameter;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Component;
import org.eclipse.uml2.uml.ConnectableElementTemplateParameter;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.DataType;
import org.eclipse.uml2.uml.Dependency;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.Extension;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.InterfaceRealization;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.OperationTemplateParameter;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PackageImport;
import org.eclipse.uml2.uml.PrimitiveType;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Realization;
import org.eclipse.uml2.uml.Reception;
import org.eclipse.uml2.uml.RedefinableTemplateSignature;
import org.eclipse.uml2.uml.Signal;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.TemplateBinding;
import org.eclipse.uml2.uml.TemplateSignature;
import org.eclipse.uml2.uml.Usage;

import com.cea.papyrus.core.IPapyrusUIConstants;
import com.cea.papyrus.core.PapyrusMessages;
import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.editor.CreationEntryFactory;
import com.cea.papyrus.core.editor.DiagramPalette;
import com.cea.papyrus.core.pseudoelement.GraphicalLink;
import com.cea.papyrus.core.pseudoelement.InstanceSpecificationLink;
import com.cea.papyrus.core.pseudoelement.Metaclass;
import com.cea.papyrus.core.ui.dialogs.PapyrusMessageDialog;
import com.cea.papyrus.core.utils.Debug;

/**
 * Utility class that can create a GEF Palette for the class diagram.
 * @see ClassDiagramPaletteFactory#createPalette()
 */
public final class ClassDiagramPaletteFactory extends CreationEntryFactory {

	/** Preference ID used to persist the palette location */
	private static final String PALETTE_DOCK_LOCATION = "ShapesEditorPaletteFactory.Location";
	
	/** Preference ID used to persist the palette size */
	private static final String PALETTE_SIZE = "ShapesEditorPaletteFactory.Size";
	
	/** Preference ID used to persist the flyout palette's state */
	private static final String PALETTE_STATE = "ShapesEditorPaletteFactory.State";

	/** Debug channel */
	private static final int debugChannel = 3;
	
	/**
	 * Create the "Shapes" drawer.
	 * @return the palette container with creation tools.
	 */
	private static PaletteContainer createShapesDrawer() {
		Debug.debug(null,"Call ClassDiagramPaletteFactory.createShapesDrawer()", debugChannel);
		PaletteDrawer componentsDrawer = new PaletteDrawer("Uml Element");

		List<PaletteEntry> entries = new ArrayList<PaletteEntry>();
		try {
			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for Package, model and profile creation
			///////////////////////////////////////////////////////////////////
			PaletteStack paletteStack = new PaletteStack("", "", null);
			
			// Package creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddPackageCommand_Label, 
					PapyrusMessages.AddPackageCommand_Description, 
					Package.class,
					null));
			
			// Model Creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddModelCommand_Label, 
					PapyrusMessages.AddModelCommand_Description, 
					Model.class, 
					null));
			
			// Profile Creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddProfileCommand_Label,
					PapyrusMessages.AddProfileCommand_Description,
					Profile.class,
					null));
			entries.add(paletteStack);
			// End of entry stack for Package, model and profile creation

			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for Class and Interface creation	
			///////////////////////////////////////////////////////////////////
			paletteStack = new PaletteStack("", "", null);

			//Action in palette for Class creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddClassCommand_Label,
					PapyrusMessages.AddClassCommand_Description,
					Class.class,
					null));
			
			//Action in palette for Interface creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddInterfaceCommand_Label,
					PapyrusMessages.AddInterfaceCommand_Description,
					Interface.class,
					null));
			
			// Action in palette for Signal creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddSignalCommand_Label,
					PapyrusMessages.AddSignalCommand_Description,
					Signal.class,
					null));
			// Action in palette for InstanceSpecification creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddInstanceSpecificationCommand_Label,
					PapyrusMessages.AddInstanceSpecificationCommand_Description,
					InstanceSpecification.class,
					null));
			entries.add(paletteStack);
			
			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for Component creation	
			///////////////////////////////////////////////////////////////////
			paletteStack = new PaletteStack("", "", null);
			
			// Action in palette Component creation
			paletteStack.add(PapyrusPlugin.creationToolFactory
					.getCombinedTemplateCreationToolEntry(
							PapyrusMessages.AddComponentCommand_Label,
							PapyrusMessages.AddComponentCommand_Description,
							Component.class, null));
			
			entries.add(paletteStack);
			// End of entry stack for Component creation	
			
			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for Property, Reception, etc.	
			///////////////////////////////////////////////////////////////////
			paletteStack = new PaletteStack("", "", null);

			// Property creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddPropertyCommand_Label,
					PapyrusMessages.AddPropertyCommand_Description,
					Property.class,
					null));
			
			// Operation creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
							PapyrusMessages.AddOperationCommand_Label,
							PapyrusMessages.AddOperationCommand_Description,
							Operation.class,
							null));
			
			// Reception creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
							PapyrusMessages.AddReceptionCommand_Label,
							PapyrusMessages.AddReceptionCommand_Description,
							Reception.class,
							null));
			entries.add(paletteStack);
			
			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for DataType, Enumeration and PrimitiveType
			///////////////////////////////////////////////////////////////////
			paletteStack = new PaletteStack("", "", null);

			// Primitive type creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddPrimitiveTypeCommand_Label,
					PapyrusMessages.AddPrimitiveTypeCommand_Description,
					PrimitiveType.class,
					null));
			
			// Datatype creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddDataTypeCommand_Label,
					PapyrusMessages.AddDataTypeCommand_Description,
					DataType.class,
					null));
			
			// Enumeration creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddEnumerationCommand_Label,
					PapyrusMessages.AddEnumerationCommand_Description,
					Enumeration.class,
					null));
			entries.add(paletteStack);

			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for Templates
			///////////////////////////////////////////////////////////////////
			paletteStack = new PaletteStack("", "", null);
			
			// TemplateSignature creation		
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddTemplateSignatureCommand_Label,
					PapyrusMessages.AddTemplateSignatureCommand_Description,
					TemplateSignature.class,
					null));

			// RedefinableTemplateSignature creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddRedefinableTemplateSignatureCommand_Label,
					PapyrusMessages.AddRedefinableTemplateSignatureCommand_Description,
					RedefinableTemplateSignature.class,
					null));
			
			// ClassifierTemplateParameter creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddClassifierTemplateParameterCommand_Label,
					PapyrusMessages.AddClassifierTemplateParameterCommand_Description,
					ClassifierTemplateParameter.class,
					null));
			
			// OperationTemplateParameter creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddOperationTemplateParameterCommand_Label,
					PapyrusMessages.AddOperationTemplateParameterCommand_Description,
					OperationTemplateParameter.class,
					null));
			
			// ConnectableElementTemplateParameter creation
			paletteStack.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddConnectableElementTemplateParameterCommand_Label,
					PapyrusMessages.AddConnectableElementTemplateParameterCommand_Description,
					ConnectableElementTemplateParameter.class,
					null));
			
			entries.add(paletteStack) ;
			
			///////////////////////////////////////////////////////////////////
			// Stereotype creation
			///////////////////////////////////////////////////////////////////
			entries.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddStereotypeCommand_Label,
					PapyrusMessages.AddStereotypeCommand_Description,
					Stereotype.class, 
					null));
			
			///////////////////////////////////////////////////////////////////
			// Metaclass import creation
			///////////////////////////////////////////////////////////////////
			entries.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddMetaclassCommand_Label,
					PapyrusMessages.AddMetaclassCommand_Description,
					Metaclass.class,
					null));

			///////////////////////////////////////////////////////////////////
			// Comment creation
			///////////////////////////////////////////////////////////////////	
			entries.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddCommentCommand_Label,
					PapyrusMessages.AddCommentCommand_Description,
					Comment.class,
					null));
			
			///////////////////////////////////////////////////////////////////
			// Constraint creation
			///////////////////////////////////////////////////////////////////	
			entries.add(PapyrusPlugin.creationToolFactory.getCombinedTemplateCreationToolEntry(
					PapyrusMessages.AddConstraintCommand_Label,
					PapyrusMessages.AddConstraintCommand_Description,
					Constraint.class,
					null));
		} catch (Exception e) {
			Debug.log(e);
		}
		componentsDrawer.addAll(entries);
		return componentsDrawer;
	}

	/**
	 * Creates the PaletteRoot and adds all palette elements for class diagrams.
	 * @return a new PaletteRoot
	 */
	public static DiagramPalette createPalette() {
		Debug.debug(null, "Call ClassDiagramPaletteFactory.createPalette()", debugChannel);
		DiagramPalette palette = new DiagramPalette(IPapyrusUIConstants.CLASS_DIAGRAM);
		palette.add(createToolsGroup(palette));
		palette.add(createShapesDrawer());
		// Create additionnal menu defined in extensions
		palette.createMenuExtensions();
		return palette;
	}

	/**
	 * Return a FlyoutPreferences instance used to save/load the preferences of a flyout palette.
	 * @return the Flyout preferences for the class diagram palettes
	 */
	static FlyoutPreferences createPalettePreferences() {
		Debug.debug(null,"Call ClassDiagramPaletteFactory.createPalettePreferences()", debugChannel);
		return new FlyoutPreferences() {
			private IPreferenceStore getPreferenceStore() {
				return Activator.getDefault().getPreferenceStore();
			}
			
			/* (non-Javadoc)
			 * @see org.eclipse.gef.ui.palette.FlyoutPaletteComposite$FlyoutPreferences#getDockLocation()
			 */
			public int getDockLocation() {
				return getPreferenceStore().getInt(PALETTE_DOCK_LOCATION);
			}
			
			/* (non-Javadoc)
			 * @see org.eclipse.gef.ui.palette.FlyoutPaletteComposite$FlyoutPreferences#getPaletteState()
			 */
			public int getPaletteState() {
				return getPreferenceStore().getInt(PALETTE_STATE);
			}
			
			/* (non-Javadoc)
			 * @see org.eclipse.gef.ui.palette.FlyoutPaletteComposite$FlyoutPreferences#getPaletteWidth()
			 */
			public int getPaletteWidth() {
				return getPreferenceStore().getInt(PALETTE_SIZE);
			}
			
			/* (non-Javadoc)
			 * @see org.eclipse.gef.ui.palette.FlyoutPaletteComposite$FlyoutPreferences#setDockLocation(int)
			 */
			public void setDockLocation(int location) {
				getPreferenceStore().setValue(PALETTE_DOCK_LOCATION, location);
			}
			
			/* (non-Javadoc)
			 * @see org.eclipse.gef.ui.palette.FlyoutPaletteComposite$FlyoutPreferences#setPaletteState(int)
			 */
			public void setPaletteState(int state) {
				getPreferenceStore().setValue(PALETTE_STATE, state);
			}
			
			/* (non-Javadoc)
			 * @see org.eclipse.gef.ui.palette.FlyoutPaletteComposite$FlyoutPreferences#setPaletteWidth(int)
			 */
			public void setPaletteWidth(int width) {
				getPreferenceStore().setValue(PALETTE_SIZE, width);
			}
		};
	}

	/**
	 * Create the "Tools" group.
	 * @param the palette where to add the elements
	 * @return the palette container with the tools group 
	 */
	private static PaletteContainer createToolsGroup(PaletteRoot palette) {
		Debug.debug(null, "Call ClassDiagramPaletteFactory.createToolsGroup()", debugChannel);
		
		PaletteGroup toolGroup = new PaletteGroup("RelationShips");

		// Add a selection tool to the group
		ToolEntry tool = new PanningSelectionToolEntry();
		toolGroup.add(tool);
		palette.setDefaultEntry(tool);

		// Add a marquee tool to the group
		toolGroup.add(new MarqueeToolEntry());

		// Add a (unnamed) separator to the group
		toolGroup.add(new PaletteSeparator());

		// list of entries to be added in the palette container
		List<PaletteEntry> entries = new ArrayList<PaletteEntry>();
		
		try {
			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for Dependencies
			///////////////////////////////////////////////////////////////////
			PaletteStack paletteStack = new PaletteStack("", "", null);
			
			// Add Dependency connection tool
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddDependencyCommand_Label,
					PapyrusMessages.AddDependencyCommand_Description,
					Dependency.class,
					null));

			// Add Abstraction connection tool
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddAbstractionCommand_Label,
					PapyrusMessages.AddAbstractionCommand_Description,
					Abstraction.class,
					null));
			

			// Add Realization connection tool
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddRealizationCommand_Label,
					PapyrusMessages.AddRealizationCommand_Description,
					Realization.class,
					null));
			
			
			// Add Usage connection tool
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddUsageCommand_Label,
					PapyrusMessages.AddUsageCommand_Description,
					Usage.class,
					null));

			// Add PackageImport
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddPackageImportCommand_Label,
					PapyrusMessages.AddPackageImportCommand_Description,
					PackageImport.class,
					null));
			
			// Add ElementImport
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddElementImportCommand_Label,
					PapyrusMessages.AddElementImportCommand_Description,
					ElementImport.class,
					null));
			
			// Add TemplateBinding
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddTemplateBindingCommand_Label,
					PapyrusMessages.AddTemplateBindingCommand_Description,
					TemplateBinding.class,
					null));			
			
			entries.add(paletteStack);	
			
			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for Generalizations
			///////////////////////////////////////////////////////////////////
			paletteStack = new PaletteStack("", "", null);

			// Add Generalization
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddGeneralizationCommand_Label,
					PapyrusMessages.AddGeneralizationCommand_Description,
					Generalization.class,
					null));


			// Add Extension
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddExtensionCommand_Label,
					PapyrusMessages.AddExtensionCommand_Description,
					Extension.class,
					null));


			entries.add(paletteStack);

			///////////////////////////////////////////////////////////////////
			// Begin of entry stack for Associations
			///////////////////////////////////////////////////////////////////
			paletteStack = new PaletteStack("", "", null);
			
			// Add association
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddAssociationCommand_Label,
					PapyrusMessages.AddAssociationCommand_Description,
					Association.class,
					null));
			

			// Add InterfaceRealization connection tool			
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddInterfaceRealizationCommand_Label,
					PapyrusMessages.AddInterfaceRealizationCommand_Description,
					InterfaceRealization.class,
					null));
			
			// Add link
			paletteStack.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddLinkCommand_Label,
					PapyrusMessages.AddLinkCommand_Description,
					InstanceSpecificationLink.class,
					null));
			
			entries.add(paletteStack);
			
			///////////////////////////////////////////////////////////////////
			// Begin of tool for Comment and constraint links
			///////////////////////////////////////////////////////////////////
			
			// Add GraphicalLink
			entries.add(PapyrusPlugin.creationToolFactory.getConnectionCreationToolEntry(
					PapyrusMessages.AddGraphicalLinkCommand_Label,
					PapyrusMessages.AddGraphicalLinkCommand_Description,
					GraphicalLink.class,
					null));				
			
		}
		catch (Exception e) {
			PapyrusMessageDialog.display(e.getMessage(), e);
		}

		toolGroup.addAll(entries);
		return toolGroup;	
	}
}